from typing import Optional, Tuple, Type
from pydantic import EmailStr
from sqlalchemy import select, Result, Row, update
from sqlalchemy.ext.asyncio.session import AsyncSession
from core.exceptions import UserWithThatEmailExistException, UserWithThatEmailNotFoundException, \
    IncorrectPasswordException, UserWithThatUserIdNotFoundException
from core.jwt import verify_password, get_password_hash
from model.user import User
from schema.user import IUserUpdate, IUserRegister, IUserRead, IUserModify


class CRUDUser:
    async def authenticate(self, user_email: EmailStr, user_password_raw: str, db_session: AsyncSession) -> int:
        user_obj = await self.get_user_by_email(user_email=user_email, db_session=db_session)
        if user_obj is None:
            raise UserWithThatEmailNotFoundException(email=user_email)
        user_obj = user_obj[0]
        if await verify_password(plain_password=user_password_raw, hashed_password=user_obj.hashed_password) is False:
            raise IncorrectPasswordException(email=user_email)
        return user_obj.id

    async def register(self, user_schema: IUserRegister, db_session: AsyncSession) -> User:
        args = user_schema.model_dump()
        obj = User(**args)
        db_session.add(obj)
        await db_session.commit()
        await db_session.refresh(obj)
        return obj

    async def modify(self, user_id: int, schema: IUserModify, db_session: AsyncSession):
        args = schema.model_dump()
        await db_session.execute(update(User).where(User.id == user_id),
                                            args)
        await db_session.commit()
        return 'ok'

    # async def create(self, user_schema: IUserCreate, db_session: AsyncSession) -> User:
    #     if (await self.get_user_by_email(user_schema.email, db_session)) is None:
    #         db_obj = User(
    #             name=user_schema.name,
    #             login=user_schema.login,
    #             password=user_schema.password,
    #             role_id=1
    #         )
    #         db_session.add(db_obj)
    #         await db_session.commit()
    #         await db_session.refresh(db_obj)
    #         return db_obj
    #     else:
    #         raise UserWithThatEmailExistException(email=user_schema.email)

    async def get_user_by_email(self, user_email: EmailStr, db_session: AsyncSession) -> Row[tuple[User]]:
        user_obj = (await db_session.execute(select(User).where(User.email == user_email))).first()
        return user_obj

    async def get_user_by_id(self, user_id: int, db_session: AsyncSession) -> Type[User] | None:
        return await db_session.get(User, user_id)


user = CRUDUser()
